package com.google.jessewilson.codesearch.intellij.configuration;

import com.google.inject.Inject;
import com.google.jessewilson.codesearch.api.SearchProvider;
import com.google.jessewilson.codesearch.api.SearchProviderFactory;
import com.google.jessewilson.codesearch.api.spi.SearchProviderConfigurationEditor;
import com.google.jessewilson.codesearch.configuration.Configuration;
import com.google.jessewilson.codesearch.configuration.ConfigurationManager;
import com.google.jessewilson.codesearch.configuration.SearchProviderConfiguration;
import com.google.jessewilson.codesearch.io.jdom.Formatter;
import com.google.jessewilson.codesearch.ui.Clipboard;
import com.google.jessewilson.codesearch.ui.EditorComponent;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.forms.layout.RowSpec;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Crudely bound configuration panel.
 *
 * @author jessewilson
 */
public class ConfigurationPanel
    implements EditorComponent<Configuration> {

  private final ConfigurationManager configurationManager;

  private final Formatter<Configuration> configurationFormatter
      = new Formatter<Configuration>(
      new Configuration.MyExternalizer(), "codesearch");
  private final Clipboard clipboard;
  private final JButton copySettingsButton;
  private final JButton pasteSettingsButton;
  private final Map<SearchProvider, CheckBoxAndEditor> checkBoxesAndEditors;

  private final JPanel panel;

  @Inject
  public ConfigurationPanel(Clipboard clipboard,
      ConfigurationManager configurationManager,
      SearchProviderFactory searchProviderFactory) {
    this.clipboard = clipboard;
    this.configurationManager = configurationManager;

    Action copyAction = new CopyAction();
    Action pasteAction = new PasteAction();

    copySettingsButton = new JButton(copyAction);
    pasteSettingsButton = new JButton(pasteAction);
    JPanel buttonsBar = new JPanel(new FlowLayout(FlowLayout.LEFT));
    buttonsBar.add(copySettingsButton);
    buttonsBar.add(pasteSettingsButton);

    checkBoxesAndEditors = new LinkedHashMap<SearchProvider, CheckBoxAndEditor>();
    for (SearchProvider searchProvider : searchProviderFactory.getAllSearchProviders()) {
      checkBoxesAndEditors.put(searchProvider,
          new CheckBoxAndEditor(searchProvider.getName(),
              searchProviderFactory.getConfigurationEditor(searchProvider)));
    }

    FormLayout formLayout = new FormLayout(
        "30px, fill:min:grow",
        "min, 30px");
    panel = new JPanel(formLayout);

    CellConstraints cc = new CellConstraints();
    panel.add(buttonsBar, cc.xywh(1, 1, 2, 1));
    panel.add(Box.createHorizontalStrut(400), cc.xywh(1, 2, 2, 1));

    int row = 3;
    for (CheckBoxAndEditor checkBoxAndEditor : checkBoxesAndEditors.values()) {
      formLayout.appendRow(new RowSpec("min"));
      formLayout.appendRow(new RowSpec("min"));
      formLayout.appendRow(new RowSpec("min"));
      formLayout.appendRow(new RowSpec("30px"));
      panel.add(new JSeparator(JSeparator.HORIZONTAL), cc.xywh(1, row, 2, 1));
      panel.add(checkBoxAndEditor.enabledToggle, cc.xywh(1, row + 1, 2, 1));
      panel.add(checkBoxAndEditor.searchProviderConfigurationEditor.getComponent(), cc.xy(2, row + 2));
      row += 4;
    }
  }

  public JPanel getComponent() {
    return panel;
  }

  public Configuration getValue() {
    Configuration.Builder builder = new Configuration.Builder();

    for (CheckBoxAndEditor checkBoxAndEditor : checkBoxesAndEditors.values()) {
      builder.addConfiguration(
          checkBoxAndEditor.searchProviderConfigurationEditor.getValue(),
          checkBoxAndEditor.enabledToggle.isSelected());
    }

    return builder.build();
  }

  public void setValue(Configuration configuration) {
    assert(configuration != null);

    for (Configuration.Entry entry : configuration.entries()) {
      SearchProviderConfiguration spConfiguration = entry.getSearchProviderConfiguration();
      CheckBoxAndEditor checkBoxAndEditor = checkBoxesAndEditors.get(spConfiguration.getSearchProvider());
      checkBoxAndEditor.searchProviderConfigurationEditor.setValue(spConfiguration);
      checkBoxAndEditor.enabledToggle.setSelected(entry.isEnabled());
    }
  }

  public boolean isModified() {
    // TODO(jessewilson): use a better binding framework to make this easy 
    return true;
  }

  private static class CheckBoxAndEditor {
    private final JCheckBox enabledToggle;
    private final SearchProviderConfigurationEditor searchProviderConfigurationEditor;

    public CheckBoxAndEditor(String toggleName,
        SearchProviderConfigurationEditor searchProviderConfigurationEditor) {
      this.enabledToggle = new JCheckBox(toggleName);
      this.searchProviderConfigurationEditor = searchProviderConfigurationEditor;
    }
  }

  /**
   * Copy settings to the clipboard.
   */
  private class CopyAction extends AbstractAction {
    public CopyAction() {
      super("Copy All", new ImageIcon(CopyAction.class.getClassLoader().getResource("actions/menu-copy.png")));
    }

    public void actionPerformed(ActionEvent e) {
      String clipboardContents = configurationFormatter.toXml(ConfigurationPanel.this.getValue());

      // copying the text somehow failed
      if (clipboardContents == null) {
        return;
      }

      clipboard.setContents(clipboardContents);
    }
  }

  /**
   * Paste settings to the clipboard.
   */
  private class PasteAction extends AbstractAction {
    public PasteAction() {
      super("Paste All", new ImageIcon(CopyAction.class.getClassLoader().getResource("actions/menu-paste.png")));
    }

    public void actionPerformed(ActionEvent e) {
      String clipboardContents = clipboard.getContents();

      // nothing to paste
      if(clipboardContents == null) {
        return;
      }

      Configuration configuration = configurationFormatter.fromXml(clipboardContents);

      // pasted data is invalid
      if(configuration == null) {
        return;
      }

      configurationManager.setCurrent(configuration);
      // TODO(jessewilson): remove this hack once we can install listeners
      setValue(configurationManager.getCurrent());
    }
  }
}
